﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.Mvc;
using Devsoft.DB.Models;
using Devsoft.Web.Mvc.Json;

namespace Devsoft.Web.Mvc.JqGrid
{
    public class GridController : JsonController
    {
        enum ComparisionOperator
        {
            Equal,
            GreaterThan,
            LessThan
        }

        protected JqGridData<T> JqGridData<T>(IEnumerable<T> data, int page = 1, int rows = 10, string sidx = null, string sord = null) where T : IModel
        {
            
            var type = typeof(T);
            
            data = FilterData(data, type);

            data = SordData(data, sidx, sord, type);
            
            var result = data.ToList();
            return new JqGridData<T>()
                {
                    rows = result.Skip((page - 1) * rows).Take(rows),
                    records = result.Count,
                    total = Convert.ToInt32(Math.Ceiling((double)result.Count() / rows)),
                    page = 1
                };
        }

        private IEnumerable<T> FilterData<T>(IEnumerable<T> data, Type type) where T : IModel
        {
            foreach (var headerName in Request.Headers.AllKeys.Where(k => k.StartsWith(JqGridHeaderPrefix)))
            {
                var propertyName = headerName.Substring(JqGridHeaderPrefix.Length);
                var comparision = ComparisionOperator.Equal;
                if (propertyName.StartsWith("min-"))
                {
                    comparision = ComparisionOperator.GreaterThan;
                    propertyName = propertyName.Substring(4);
                }
                else if (propertyName.StartsWith("max-"))
                {
                    comparision = ComparisionOperator.LessThan;
                    propertyName = propertyName.Substring(4);
                }

                var property = type.GetProperty(propertyName);
                var filterValue = Request.Headers[headerName];
                if (property != null)
                {
                    if (property.PropertyType == typeof (DateTime))
                    {
                        var date = DateTime.Parse(filterValue);
                        var minDate = date.Date;
                        var maxDate = minDate.AddDays(1);
                        data = data.Where(x => Between((DateTime) property.GetValue(x, emptyArgs), minDate, maxDate));
                    }
                    else
                    {
                        switch (comparision)
                        {
                            case ComparisionOperator.GreaterThan:
                                switch (property.PropertyType.FullName)
                                {
                                    case "System.Int32":
                                        int intVal = Convert.ToInt32(filterValue);
                                        data = data.Where(x => (int) property.GetValue(x, emptyArgs) >= intVal);
                                        break;
                                    case "System.DateTime":
                                        DateTime dateVal = DateTime.Parse(filterValue);
                                        data = data.Where(x => (DateTime) property.GetValue(x, emptyArgs) >= dateVal);
                                        break;
                                    default:
                                        throw new NotImplementedException();
                                }
                                break;
                            case ComparisionOperator.LessThan:
                                switch (property.PropertyType.FullName)
                                {
                                    case "System.Int32":
                                        int intVal = Convert.ToInt32(filterValue);
                                        data = data.Where(x => (int) property.GetValue(x, emptyArgs) <= intVal);
                                        break;
                                    case "System.DateTime":
                                        DateTime dateVal = DateTime.Parse(filterValue);
                                        data = data.Where(x => (DateTime) property.GetValue(x, emptyArgs) <= dateVal);
                                        break;
                                    default:
                                        throw new NotImplementedException();
                                }
                                break;
                            default:
                                data =
                                    data.Where(
                                        x =>
                                        property.GetValue(x, emptyArgs).ToString().IndexOf(filterValue,
                                                                                           StringComparison.
                                                                                               CurrentCultureIgnoreCase) > -1);
                                break;
                        }
                    }
                }
            }
            return data;
        }

        private static IEnumerable<T> SordData<T>(IEnumerable<T> data, string sidx, string sord, Type type)
            where T : IModel
        {
            if (!string.IsNullOrEmpty(sidx))
            {
                PropertyInfo sortProperty;
                var pos = sidx.IndexOf(",");
                if (pos > -1)
                {
                    var tmp = sidx.Split(',');
                    var tmp2 = tmp[0].Split(' ');
                    var groupProperty = type.GetProperty(tmp2[0].Trim());
                    if (groupProperty != null)
                    {
                        var data1 = tmp2[1] == "desc"
                                        ? data.OrderByDescending(x => groupProperty.GetValue(x, emptyArgs))
                                        : data.OrderBy(x => groupProperty.GetValue(x, emptyArgs));
                        sidx = tmp[1].Trim();
                        sortProperty = string.IsNullOrEmpty(sidx) ? null : type.GetProperty(sidx);
                        if (sortProperty != null)
                        {
                            data = sord == "desc"
                                       ? data1.ThenByDescending(x => sortProperty.GetValue(x, emptyArgs))
                                       : data1.ThenBy(x => sortProperty.GetValue(x, emptyArgs));
                        }
                        else
                        {
                            data = data1;
                        }
                    }
                }
                else
                {
                    sortProperty = string.IsNullOrEmpty(sidx) ? null : type.GetProperty(sidx);
                    if (sortProperty != null)
                    {
                        data = sord == "desc"
                                   ? data.OrderByDescending(x => sortProperty.GetValue(x, emptyArgs))
                                   : data.OrderBy(x => sortProperty.GetValue(x, emptyArgs));
                    }
                }
            }
            return data;
        }

        protected JqGridResponse JqGrid<T>(IEnumerable<T> data, int page = 1, int rows = 10, string sidx = null, string sord = null, Func<T, object> idGetter = null, Func<T, object>[] cols = null) where T : IModel
        {
            return JqGridData(data, page, rows, sidx, sord).ToJqGridResponse(idGetter,cols);
        }

        protected bool Between<T>(T x, T min, T max) where T : IComparable<T>
        {
            return x.CompareTo(min) >= 0 && x.CompareTo(max) <= 0;
        }

        private const string JqGridHeaderPrefix = "jqgrid-filter-";
        private static  object[] emptyArgs = new object[] { };
    }
}
